/*
  Copyright 2007 Anders Båtstrand

      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.

 */
package org.dyndns.anderlin.jmplayer.client.ui;

import org.dyndns.anderlin.jmplayer.model.Artist;
import org.dyndns.anderlin.jmplayer.service.ArtistService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.binding.form.HierarchicalFormModel;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.richclient.application.PageComponentContext;
import org.springframework.richclient.application.event.LifecycleApplicationEvent;
import org.springframework.richclient.application.support.AbstractView;
import org.springframework.richclient.command.CommandGroup;
import org.springframework.richclient.command.support.AbstractActionCommandExecutor;
import org.springframework.richclient.command.support.GlobalCommandIds;
import org.springframework.richclient.dialog.CompositeDialogPage;
import org.springframework.richclient.dialog.ConfirmationDialog;
import org.springframework.richclient.dialog.TabbedDialogPage;
import org.springframework.richclient.dialog.TitledPageApplicationDialog;
import org.springframework.richclient.form.FormModelHelper;
import org.springframework.richclient.progress.TreeStatusBarUpdater;
import org.springframework.richclient.tree.FocusableTreeCellRenderer;
import org.springframework.richclient.util.PopupMenuMouseListener;
import org.springframework.util.Assert;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;

public class ArtistManagerView extends AbstractView implements InitializingBean, ApplicationListener {

    private JTree artistTree;

    private DefaultTreeModel artistTreeModel;

    private ArtistService artistService;

    private DeleteExecutor deleteExecutor = new DeleteExecutor();

    private PropertiesExecutor propertiesExecutor = new PropertiesExecutor();

    private ArtistPropertiesExecutor artistPropertiesExecutor = new ArtistPropertiesExecutor();

    public void setArtistService(ArtistService artistService) {
        this.artistService = artistService;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(artistService, "A artistService must be provided.");
    }

    protected JComponent createControl() {
        JPanel view = new JPanel(new BorderLayout());
        createArtistManagerTree();
        JScrollPane sp = new JScrollPane(artistTree);
        view.add(sp, BorderLayout.CENTER);
        return view;
    }

    private void createArtistManagerTree() {
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Artists");
        Collection<Artist> artists = artistService.getAllArtists();
        for (Artist artist : artists) {
            DefaultMutableTreeNode artistNode = new DefaultMutableTreeNode(artist);
            // This would have loaded ALL images and imageVersion. Instead loadon demand
            /* for (Image image : artist.getImages()) {
               DefaultMutableTreeNode imageNode = new DefaultMutableTreeNode(image);
               artistNode.add(imageNode);
               for (ImageVersion imageVersion : image.getVersions()) {
                   imageNode.add(new DefaultMutableTreeNode(imageVersion));
               }
           } */
            rootNode.add(artistNode);
        }
        this.artistTreeModel = new DefaultTreeModel(rootNode);
        this.artistTree = new JTree(artistTreeModel);
        artistTree.setShowsRootHandles(true);
        artistTree.addTreeSelectionListener(new TreeStatusBarUpdater(getStatusBar()) {
            public String getSelectedObjectName() {
                Artist selectedArtist = getSelectedArtist();
                if (selectedArtist != null) {
                    return selectedArtist.getFirstname() + " " + selectedArtist.getSurname();
                }
                return "Artists";
            }
        });
        artistTree.addTreeSelectionListener(new TreeSelectionListener() {
            public void valueChanged(TreeSelectionEvent e) {
                updateCommands();

                // hour-glass while fetching
                artistTree.setCursor(new Cursor(Cursor.WAIT_CURSOR));
                /**
                 // fetch children of the selected object
                 if (getSelectedArtist() != null) {
                 Artist p = getSelectedArtist();
                 DefaultMutableTreeNode n = getSelectedArtistNode();
                 for (Image i : p.getImages()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 } else if (getSelectedImage() != null) {
                 Image p = getSelectedImage();
                 DefaultMutableTreeNode n = getSelectedImageNode();
                 for (ImageVersion i : p.getVersions()) {
                 n.add(new DefaultMutableTreeNode(i));
                 }
                 }    **/
                // finnished
                artistTree.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
        });
        artistTree.addMouseListener(new PopupMenuMouseListener() {
            protected boolean onAboutToShow(MouseEvent e) {
                return !isRootOrNothingSelected();
            }

            protected JPopupMenu getPopupMenu() {
                return getSelectedArtist() != null ? createArtistPopupContextMenu() : null;
            }
        });
        artistTree.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1 && propertiesExecutor.isEnabled()) {
                    propertiesExecutor.execute();
                }
            }
        });
        artistTree.setCellRenderer(getTreeCellRenderer());
        artistTree.setRootVisible(true);
    }

    private void updateCommands() {
        int treeSelectionCount = artistTree.getSelectionCount();
        if (isRootOrNothingSelected()) {
            deleteExecutor.setEnabled(false);
            propertiesExecutor.setEnabled(false);
        } else if (treeSelectionCount == 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(true);
        } else if (treeSelectionCount > 1) {
            deleteExecutor.setEnabled(true);
            propertiesExecutor.setEnabled(false);
        }
    }

    private boolean isRootOrNothingSelected() {
        return artistTree.getSelectionCount() == 0
                || (artistTree.getSelectionCount() == 1 && artistTree.isRowSelected(0));
    }

    private Artist getSelectedArtist() {
        DefaultMutableTreeNode node = getSelectedArtistNode();
        if (node != null)
            return (Artist) node.getUserObject();

        return null;
    }

    private DefaultMutableTreeNode getSelectedArtistNode() {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) artistTree.getLastSelectedPathComponent();
        if (node == null || !(node.getUserObject() instanceof Artist))
            return null;

        return node;
    }

    private DefaultTreeCellRenderer treeCellRenderer = new FocusableTreeCellRenderer() {
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
                                                      boolean leaf, int row, boolean hasFocus) {
            super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            if (node.isRoot()) {
                this.setIcon(getIconSource().getIcon("folder.icon"));
            } else if (node.getUserObject() instanceof Artist) {
                Artist artist = (Artist) node.getUserObject();
                this.setText(artist.getFirstname() + " " + artist.getSurname());
                this.setIcon(getIconSource().getIcon("artist.bullet"));
            }
            return this;
        }
    };

    public TreeCellRenderer getTreeCellRenderer() {
        return treeCellRenderer;
    }

    protected void registerLocalCommandExecutors(PageComponentContext context) {
        context.register(GlobalCommandIds.DELETE, deleteExecutor);
        context.register(GlobalCommandIds.PROPERTIES, propertiesExecutor);
    }

    /**
     * Handle an application event.
     *
     * @param e the event to respond to
     */

    public void onApplicationEvent(ApplicationEvent e) {
        if (e instanceof LifecycleApplicationEvent) {
            LifecycleApplicationEvent le = (LifecycleApplicationEvent) e;
            if (le.getEventType().equals(LifecycleApplicationEvent.CREATED) && le.objectIs(Artist.class)) {
                if (artistTree != null) {
                    DefaultMutableTreeNode root = (DefaultMutableTreeNode) artistTreeModel.getRoot();
                    root.add(new DefaultMutableTreeNode(le.getObject()));
                    artistTreeModel.nodeStructureChanged(root);
                }
            }
        }
    }

    private JPopupMenu createArtistPopupContextMenu() {
        // delete, addImage separator, properties
        CommandGroup group = getWindowCommandManager().createCommandGroup(
                "artistViewTableArtistCommandGroup",
                new Object[]{"deleteCommand", "separator",
                        "propertiesCommand"});
        return group.createPopupMenu();
    }

    private class PropertiesExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            if (getSelectedArtist() != null) {
                artistPropertiesExecutor.execute();
            }
        }
    }

    private class DeleteExecutor extends AbstractActionCommandExecutor {
        public void execute() {
            int artistCount = 0;
            int imageCount = 0;
            int imageVersionCount = 0;
            final java.util.List<DefaultMutableTreeNode> nodesToDelete = new ArrayList<DefaultMutableTreeNode>();
            TreePath[] paths = artistTree.getSelectionPaths();
            for (TreePath path : paths) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
                if (node.isRoot()) {
                    continue;
                }
                if (node.getUserObject() instanceof Artist) {
                    artistCount++;
                }
                nodesToDelete.add(node);
            }

            ConfirmationDialog dialog = new ConfirmationDialog() {
                protected void onConfirm() {
                    for (DefaultMutableTreeNode node : nodesToDelete) {
                        artistTreeModel.removeNodeFromParent(node);
                        if (node.getUserObject() instanceof Artist) {
                            artistService.remove((Artist) node.getUserObject());
                        }
                    }
                }
            };
            if (artistCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteArtistDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteArtistDialog.label"));
            } else if (imageCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageDialog.label"));
            } else if (imageVersionCount > 0) {
                dialog.setTitle(getMessage("confirmDeleteImageVersionDialog.title"));
                dialog.setConfirmationMessage(getMessage("confirmDeleteImageVersionDialog.label"));
            }

            dialog.showDialog();
        }
    }

    // TODO: Move all usefull classes out of this mess!
    private class ArtistPropertiesExecutor extends AbstractActionCommandExecutor {

        private HierarchicalFormModel artistFormModel;

        private ArtistForm artistForm;

        private CompositeDialogPage compositePage;

        public void execute() {
            final Artist artist = getSelectedArtist();
            artistFormModel = FormModelHelper.createCompoundFormModel(artist);
            artistForm = new ArtistForm(FormModelHelper.createChildPageFormModel(artistFormModel, null));

            compositePage = new TabbedDialogPage("artistProperties");
            compositePage.addForm(artistForm);

            TitledPageApplicationDialog dialog = new TitledPageApplicationDialog(compositePage, getWindowControl()) {
                protected void onAboutToShow() {
                    artistForm.requestFocusInWindow();
                    setEnabled(compositePage.isPageComplete());
                }

                protected boolean onFinish() {
                    artistFormModel.commit();
                    artistService.saveOrUpdate(artist);
                    artistTreeModel.nodeChanged(getSelectedArtistNode());
                    return true;
                }
            };
            dialog.showDialog();
        }
    }

}